#ifndef __M_DB_H__
#define __M_DB_H__
#include"util.hpp"
#include<mutex>
#include<cassert>
//创建操作表的类，不同的表用不同的类操作，这样就方便管理和使用,每个表实例化出一个对象
class user_table
{
private:
    std::mutex _mutex;//查询时查询到结果和把结果放到变量里应该是原子的，不然只是查到了但是变量里并没有数据，然后就被切走了
    MYSQL* _mysql;//这是一个句柄，关于mysql的使用要用到

public:
    //构造函数 mysql 的默认端口号是3306
    user_table(const std::string &password, const std::string &dbname, const std::string &host, const std::string &user, const uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(password,dbname,host,user,port);
        assert(_mysql != nullptr);

    }
    
    //析构函数
    ~user_table()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = nullptr;
    }
    //1.创建用户（注册）
    bool insert(Json::Value& user)
    {
        //1.sql语句，用buf存取，sprintf占位符

        if(user["username"].empty() || user["password"].empty())
        {
            DLOG("haven't username or password entried");
            return false;
        }
#define USER_INSERT "insert user values(null,'%s',password('%s'),1000,0,0);"        
        char buf[4096]= {0};
        sprintf(buf,USER_INSERT,user["username"].asCString(),user["password"].asCString());
        //这里不需要对注册查重，因为我们user表中的username是unique key
        bool exe = mysql_util::mysql_exec(_mysql,buf);
        if(exe == false)
        {
            DLOG("insert userinfo failed");
            return false;
        }
        
        return true;
    }
    //2.登录
    bool login(Json::Value& user)
    {

        //检查user表中是否有user用户
        if(user["password"].empty() || user["username"].empty())
        {
            DLOG("haven't username or password entried");
            return false;
        }
        //关键字之类的比如 where都只用一个就可以了别多写
#define USER_LOGIN "select id, username, score, win_count, total_count from user where username='%s' and  password=password('%s');"
        char buf[4096]= {0};
        sprintf(buf,USER_LOGIN,user["username"].asCString(),user["password"].asCString());
        //登录后还要给网页返回一个用户信息表
        MYSQL_RES *res = nullptr;
        //单独打开一个作用域，然后用unique_mutex锁这块区域，说明这块区域操作要求是原子的
        {
            std::unique_lock<std::mutex> uq(_mutex);
            //执行查询
            bool exe = mysql_util::mysql_exec(_mysql,buf);
            if(exe == false)
            {
                DLOG("login user failed");
                return false;
            }
            //获取数据
            res = mysql_store_result(_mysql);
            if(res == nullptr)
            {
                DLOG("user not existed");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if(row_num != 1)
        {
            //说明找到两条以上数据
            DLOG("user info not unique !");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["username"] = row[1];
        user["score"] = (Json::UInt64)std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        user["total_count"] = std::stoi(row[4]);
        //res的结果也是动态开辟的所以要关掉
        mysql_free_result(res);
        return true;
    }
    //3.用户数据查询
    //3.1 根据username查询
    bool select_user_by_name(const std::string& name,Json::Value& user)
    {
        //通过name查找数据 通过user返回
#define SELECT_BY_NAME "select id,username,score,win_count,total_count from user where username='%s';"
        char buf[4096] = {0};
        sprintf(buf,SELECT_BY_NAME,name.c_str());
        //登录后还要给网页返回一个用户信息表
        MYSQL_RES *res = nullptr;//作用域的变量是局部的，这个变量是之后要使用的所以在外面定义
        //单独打开一个作用域，然后用unique_mutex锁这块区域，说明这块区域操作要求是原子的
        {
            std::unique_lock<std::mutex> uq(_mutex);
            //执行查询
            bool exe = mysql_util::mysql_exec(_mysql,buf);
            if(exe == false)
            {
                DLOG("select by username failed");
                return false;
            }
            //获取数据
            res = mysql_store_result(_mysql);
            if(res == nullptr)
            {
                DLOG("user not existed");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if(row_num != 1)
        {
            //说明找到两条以上数据
            DLOG("user info not unique !");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["username"] = row[1];
        user["score"] = (Json::UInt64)std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        user["total_count"] = std::stoi(row[4]);
        //res的结果也是动态开辟的所以要关掉
        mysql_free_result(res);
        return true;
    }
    //3.2 根据id查询
    bool select_user_by_id(const uint64_t id,Json::Value& user)
    {
        //通过ID查找数据 通过user返回
#define SELECT_BY_ID "select id,username,score,win_count,total_count from user where id=%ld;"
        char buf[4096] = {0};
        sprintf(buf,SELECT_BY_ID,id);
        //登录后还要给网页返回一个用户信息表
        MYSQL_RES *res = nullptr;//作用域的变量是局部的，这个变量是之后要使用的所以在外面定义
        //单独打开一个作用域，然后用unique_mutex锁这块区域，说明这块区域操作要求是原子的
        {
            std::unique_lock<std::mutex> uq(_mutex);
            //执行查询
            bool exe = mysql_util::mysql_exec(_mysql,buf);
            if(exe == false)
            {
                DLOG("select by id failed");
                return false;
            }
            //获取数据
            res = mysql_store_result(_mysql);
            if(res == nullptr)
            {
                DLOG("user not existed");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if(row_num != 1)
        {
            //说明找到两条以上数据
            DLOG("user info not unique !");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        //从表中获取的数据都是字符串
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["username"] = row[1];
        user["score"] = (Json::UInt64)std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        user["total_count"] = std::stoi(row[4]);
        //res的结果也是动态开辟的所以要关掉
        mysql_free_result(res);
        return true;
    }
    //4.更新数据
    //4.1胜利更新
    bool win_update(uint64_t id)
    {
#define WIN_UPDATE "update user set score=score+50, win_count=win_count+1, total_count=total_count+1 where id=%ld"
        char sql[4096] = {0};
        sprintf(sql,WIN_UPDATE,id);
        int exe = mysql_util::mysql_exec(_mysql,sql);
        if(exe == false)
        {
            //代表没有执行成功
            DLOG("win update exec failed");
            return false;
        }
        return true;
    }
    //4.2失败更新
    bool lose_update(uint64_t id)
    {
#define LOSE_UPDATE "update user set score=score-50, total_count=total_count+1 where id=%ld"
        char sql[4096] = {0};
        sprintf(sql,LOSE_UPDATE,id);
        int exe = mysql_util::mysql_exec(_mysql,sql);
        if(exe == false)
        {
            //代表没有执行成功
            DLOG("win update exec failed");
            return false;
        }
        return true;
    }
};


#endif